
using namespace std;

#include <iostream>
#include <ctime>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#ifdef GMPI
#include <mpi.h>

#include "gsolverBBPv3NoWaitSlave.h"
#endif

#include "gsupplyLinkProblem.h"
#include "gsolverBBPv3NoWait.h"
#include "gsolverBBPv3NoWaitMaster.h"
#include "gchrono.h"


/**
 * Global Variable : problem to solve
 */
GSupplyLinkProblem slpb ;
int bbpIsWaitingForMPIComm = 1 ;

int minPercentForDispatching=DEFAULT_MIN_PERCENT_FOR_DISPATCHING ;
int maxPercentForDispatching=DEFAULT_MAX_PERCENT_FOR_DISPATCHING ;
double periodMPITestSlave   =DEFAULT_PERIOD_MPI_TEST_SLAVE ;
double periodMPITestMaster  =DEFAULT_PERIOD_MPI_TEST_MASTER ;

int onlyNewBatchFirst=0 ;
int lowerbound = 0 ;


/** 
 *
 */
void helpCommand() {
#ifdef GMPI
  cout << "" << endl ;
  cout << "-----------------------------------------" << endl ;
  cout << "! MPI samebatch multiproduct binpacking !" << endl ;
  cout << "-----------------------------------------" << endl ;
  cout << "" << endl ;
  cout << "Usage of samebatch-multiproduct-binpacking-mpi version 1.0:" << endl ;
  cout << "samebatch-multiproduct-binpacking-mpi [options]" << endl ;
#else
  cout << "" << endl ;
  cout << "-----------------------------------------" << endl ;
  cout << "! SEQ samebatch multiproduct binpacking !" << endl ;
  cout << "-----------------------------------------" << endl ;
  cout << "" << endl ;
  cout << "Usage of samebatch-multiproduct-binpacking version 1.0:" << endl ;
  cout << "samebatch-multiproduct-binpacking [options]" << endl ;
#endif
  cout << "" << endl ;
  cout << "List of options:" << endl ;
  cout << "-h                               \t prints this message" << endl ;
  cout << "-bbp <pbfilename>                \t solves a multi-product problem with bbp solver (MPI nowait)" << endl ;
  cout << "-bbpnowait <pbfilename>          \t solves a multi-product problem with bbp solver (MPI nowait)" << endl ;
  cout << "-bbpwait <pbfilename>            \t solves a multi-product problem with bbp solver (MPI wait)" << endl ;
  cout << endl ;
  cout << "-minPercentForDispatching perc   \t defines the min depth level for dispatching work (as a percentage)" << endl ;
  cout << "-maxPercentForDispatching perc   \t defines the max depth level for dispatching work (as a percentage)" << endl ;
  cout << "-periodMPITestMaster      period \t defines the periodic rate in ms for the master to test for a MPI comm" << endl ;
  cout << "-periodMPITestSlave       period \t defines the periodic rate in ms for the slave to test for a MPI comm" << endl ;
  cout << endl ;
  cout << "-onlyNewBatchFirst               \t new batches are created only at the first position. " << endl ;
  cout << "-lowerboundV0                    \t lowerbound is the partial evaluation of the solution (this is very bad and the default) " << endl ;
  cout << "-lowerboundV1                    \t L1 for binpacking pb and light LB for LSDSP problem" << endl ;
  cout << "                                 \t This can lead to a non optimal solution." << endl ;
  cout << "" << endl ;

}

/**
 * Argument Line Parser
 */
int argumentLineParser(int argc, char* argv[], int node=0) {
  int exitcode=0 ;
  int result = 1;
  if (argc>0) {
    int p = 1 ;
    while (p<argc) {
      if ( !strcmp(argv[p],"-h") || !strcmp(argv[p],"-help") ) {
	if (!node) helpCommand() ;
      }


      // Solve BBP Problem v3 with master and slave NOT WAITING for MPI Communication
      else if ( !strcmp(argv[p],"-bbp") ||  !strcmp(argv[p],"-bbpnowait") ) {
	bbpIsWaitingForMPIComm = 0 ;
	if (p+1<argc) { slpb.open(argv[++p],node) ; result = 0 ; }
	else if (!node) cout << "ERROR Problem filename missing" << endl ;
      }



      else if ( !strcmp(argv[p],"-onlyNewBatchFirst") ) {
	onlyNewBatchFirst = 1 ;
      }


      else if ( !strcmp(argv[p],"-lowerboundV1") ) {
	lowerbound = 1 ;
      }


      else if ( !strcmp(argv[p],"-minPercentForDispatching") ) {
	if (p+1<argc) { minPercentForDispatching = atoi(argv[++p]) ; }
	else if (!node) cout << "ERROR minPercentForDispatching value is missing" << endl ;
      }


      else if ( !strcmp(argv[p],"-maxPercentForDispatching") ) {
	if (p+1<argc) { maxPercentForDispatching = atoi(argv[++p]) ; }
	else if (!node) cout << "ERROR maxPercentForDispatching value is missing" << endl ;
      }


      else if ( !strcmp(argv[p],"-periodMPITestSlave") ) {
	if (p+1<argc) { periodMPITestSlave = (double)(atof(argv[++p])) ; }
	else if (!node) cout << "ERROR periodMPITest value is missing" << endl ;
      }


      else if ( !strcmp(argv[p],"-periodMPITestMaster") ) {
	if (p+1<argc) { periodMPITestMaster = (double)(atof(argv[++p])) ; }
	else if (!node) cout << "ERROR periodMPITest value is missing" << endl ;
      }


      else {
	if (!node) cout << "Argument line error : args[" << p << "]=" << argv[p] << endl ;
      }
      p++ ;
    }
  }

  if (!node) {
    if (maxPercentForDispatching<0 || maxPercentForDispatching >100) {
      cout << "ERROR maxPercentForDispatching should bin in [0,100]" << endl ;
    }
    if (minPercentForDispatching<0 || minPercentForDispatching>100) {
      cout << "ERROR minPercentForDispatching should bin in [0,100]" << endl ;
      exitcode = 1 ;
    }
    if (minPercentForDispatching>maxPercentForDispatching) {
      cout << "ERROR minJobForDispatching("<<minPercentForDispatching<<")>" ;
      cout << "maxPercentForDispatching(" << maxPercentForDispatching<< ")"<<endl ;
      exitcode = 1 ;
    }
    if (periodMPITestSlave<0) {
      cout << "ERROR periodMPITestSlave should be >= 0 " <<endl ;
      exitcode = 1 ;
    }
    if (periodMPITestMaster<0) {
      cout << "ERROR periodMPITestMaster should be >= 0 " <<endl ;
      exitcode = 1 ;
    }
  }

  if (exitcode) {
#ifdef GMPI
    MPI_Finalize();
#endif
    exit(exitcode) ;
  }
 
  return result ;
}





/**
 *
 */
int main(int argc, char* argv[])
{
  int node=0, p=1;

#ifdef GMPI
  MPI_Init(&argc,&argv);

  MPI_Comm_size(MPI_COMM_WORLD, &p);
  MPI_Comm_rank(MPI_COMM_WORLD, &node);
#endif

  if (!argumentLineParser (argc, argv,node)) {
    if (node==0) {
      cout << "Here is the MASTER with " << p << " nodes !!!" << endl << flush ;
      cout << "onlyNewBatchFirst=" << onlyNewBatchFirst << endl ;
      cout << "lowerBoundLevel=V" << lowerbound << endl ;
    }
    //else cout << "Here is the slave from node " << node << endl << flush ;

    if (!node) {
      GSolverBBPv3NoWaitMaster bbpmaster (slpb,node,p) ;
      bbpmaster.setMinMaxJobForDispatchingParameter(minPercentForDispatching,maxPercentForDispatching) ;
      bbpmaster.setPeriodMPITestParameter(periodMPITestSlave, periodMPITestMaster) ;
      bbpmaster.setOnlyNewBatchFirst(onlyNewBatchFirst) ;
      if (lowerbound) bbpmaster.setLowerBoundLevel(lowerbound) ;
      bbpmaster.solve() ;
    }
#ifdef GMPI
    else {
      GSolverBBPv3NoWaitSlave bbpslave (slpb,node,p) ;
      bbpslave.setMinMaxJobForDispatchingParameter(minPercentForDispatching,maxPercentForDispatching) ;
      bbpslave.setPeriodMPITestParameter(periodMPITestSlave, periodMPITestMaster) ;
      bbpslave.setOnlyNewBatchFirst(onlyNewBatchFirst) ;
      if (lowerbound) bbpslave.setLowerBoundLevel(lowerbound) ;
      bbpslave.solve() ;
    }
#endif

  }
  else {
    if (!node) helpCommand() ;
  }

#ifdef GMPI
  MPI_Finalize();
#endif


  return 0;
}


